﻿using System.Collections.Generic;
using System.Linq;
using GameStateManagement.Model.Entities;
using GameStateManagement.Model.Physics.Interfaces;
using GameStateManagement.View.Rendering;
using Microsoft.Xna.Framework;

namespace GameStateManagement.Model.Physics
{
    public class PhysicsModule2D : PhysicsModule
    {
        #region Fields

        protected Vector2 Resultant;

        #endregion

        #region Properties

        public readonly RenderManager RenderManager;

        #endregion

        #region Constructors

        public PhysicsModule2D(RenderManager renderManager)
        {
            GlobalForces = new List<PhysicsForce>();
            _forceFields = new List<PhysicsForceField>();
            ForceGenerators = new List<ForceGenerator>();
            RenderManager = renderManager;
        }

        #endregion

        #region Public Methods

        public override void ApplyForcesOnEntites(GameTime gameTime, CompositeEntity entities)
        {
            UpdateResultant();

            foreach (EntityBase entity in entities)
            {
                if (entity is MovingEntity)
                {
                    var movingEntity = entity as MovingEntity;

                    //Get the total resultant for the entity.
                    Vector2 entityResultant = GetResultantForEntity(movingEntity);

                    //Depending on elapsed time force effect will differ.
                    Vector2 modifiedByTime = Vector2.Multiply(entityResultant,
                                                              (float)gameTime.ElapsedGameTime.TotalSeconds);

                    movingEntity.ApplyForceResultant(modifiedByTime);
                }
            }

            CheckCollisionsOnEntities(entities);
        }

        #endregion

        #region Physics Methods

        private Vector2 GetResultantForEntity(MovingEntity movingEntity)
        {
            Vector2 fieldResultant = CheckForceFields(movingEntity);

            Vector2 entityResultant = Vector2.Add(Resultant, fieldResultant);

            return entityResultant / movingEntity.Mass;
        }

        protected override void UpdateResultant()
        {
            Resultant = Vector2.Zero;
            foreach (PhysicsForce2D force in GlobalForces)
            {
                Vector2 vector = Vector2.Multiply(force.Direction, force.Magnitude);

                Resultant = Vector2.Add(Resultant, vector);
            }
        }

        protected override Vector2 CheckForceFields(MovingEntity entity)
        {
            return _forceFields.Select(field => field.GetForceFieldResultantForEntity(entity)).Aggregate(Vector2.Zero, Vector2.Add);
        }

        #region Collision Methods

        protected override void CheckCollisionsOnEntities(CompositeEntity entities)
        {
            var alreadyTested = new int[entities.Count * 2];

            for (int j = 0; j < entities.Count; j++)
                for (int k = 0; k < entities.Count; k++)
                {
                    if (j != k && alreadyTested[j + k] != 1)
                    {
                        alreadyTested[j + k] = 1;

                        var jEntity = (ICollidable)entities.ElementAt(j);
                        var kEntity = (ICollidable)entities.ElementAt(k);

                        var collision = new Collision(RenderManager, jEntity, kEntity);

                        if (collision.CollisionHasOccured())
                        {
                            var collisionResolver = new CollisionResolver(jEntity, kEntity);
                            collisionResolver.ResolveCollision();
                            OnCollisionOccured(new CollisionEventArgs(jEntity, kEntity));
                        }
                    }
                }
        }

        #endregion

        #endregion
    }
}